use rayon::prelude::*;
use std::sync::Arc;
use wasm_bindgen::prelude::*;
use web_sys::window;

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

macro_rules! console_log {
    ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}

fn get_current_time_ms() -> f64 {
    let window = window().expect("should have a window in this context");
    let performance = window
        .performance()
        .expect("performance should be available");

    performance.now()
}

// 定义过滤规则的枚举类型
#[derive(Debug, Clone, Copy)]
enum FilterRule {
    TrimWhitespace = 1,
    IgnoreCase = 2,
}

impl From<i32> for FilterRule {
    fn from(value: i32) -> Self {
        match value {
            1 => Self::TrimWhitespace,
            2 => Self::IgnoreCase,
            _ => panic!("Invalid filter rule: {value}"),
        }
    }
}

fn _filter_logs(
    logs: Vec<String>,
    search_terms: Vec<String>,
    filter_rules: Vec<i32>,
) -> Vec<String> {
    if search_terms.is_empty() {
        return logs;
    }

    // 将i32规则转为枚举类型
    let filter_rules: Vec<FilterRule> = filter_rules.into_iter().map(FilterRule::from).collect();

    // 预处理搜索词
    let processed_search_terms = {
        let mut terms = search_terms;
        for &rule in &filter_rules {
            match rule {
                FilterRule::TrimWhitespace => {
                    // 去除前后空格
                    terms = terms.iter().map(|term| term.trim().to_string()).collect();
                }
                FilterRule::IgnoreCase => {
                    // 忽略大小写 - 这个规则在预处理中不应用，因为需要针对每个日志条目
                    // 我们只预处理搜索词的大小写
                    terms = terms.iter().map(|term| term.to_lowercase()).collect();
                }
            }
        }
        Arc::new(terms)
    };

    // 创建引用版本以避免clone
    let search_terms_ref: Vec<&str> = processed_search_terms.iter().map(|s| s.as_str()).collect();

    logs.into_par_iter()
        .filter(|log| {
            let mut processed_log = log.as_str();
            // 对于每个日志条目，我们创建一个临时的处理后字符串
            let mut owned_processed_log;

            // 应用需要针对单个日志的规则
            for &rule in &filter_rules {
                if let FilterRule::IgnoreCase = rule {
                    // 忽略大小写
                    owned_processed_log = processed_log.to_lowercase();
                    processed_log = &owned_processed_log;
                }
            }

            // 检查是否包含任一处理后的搜索词
            search_terms_ref
                .iter()
                .any(|term| processed_log.contains(term))
        })
        .collect()
}

#[wasm_bindgen]
pub fn filter_logs(
    logs: Vec<String>,
    search_terms: Vec<String>,
    filter_rules: Vec<i32>,
) -> Vec<String> {
    let t_s = get_current_time_ms();
    let _after_filtered = _filter_logs(logs, search_terms, filter_rules);
    let elapsed_time = get_current_time_ms() - t_s;
    console_log!("[rust]-Filter logs took: {} ms", elapsed_time);

    _after_filtered
}

#[cfg(test)]
mod tests {
    use crate::_filter_logs;

    #[test]
    fn test_filter_logs() {
        let logs = vec![
            "936a76af8d47   databoard-slim:latest                           \"bash -c 'source .ve…\"   2 weeks ago    Up 2 weeks             0.0.0.0:9001->9001/tcp, :::9001->9001/tcp ".to_string(),
            "a4ecb5759b0b   jdk1.8tz                                        \"java -jar -Xms512m …\"   3 weeks ago    Up 2 weeks             0.0.0.0:18058->18058/tcp, :::18058->18058/tcp".to_string(),
            "2024-12-25 15:57:52.5752 | DEBUG | utils.remote_call:50 - [Request Url] ==> http://192.100.8.19:3752/optimized/optimizeTask/calc/taskExecuteCallBack".to_string(),
        ];

        let search_terms = vec!["[request Url]".to_string(), "bash".to_string()];
        let logs_ = _filter_logs(logs.clone(), search_terms, Vec::from([2]));
        println!("{:?} {:?}", logs_, logs_.len());
        assert_eq!(logs_.len(), 2);

        let search_terms1 = vec!["[request Url]".to_string()];
        let logs1 = _filter_logs(logs.clone(), search_terms1, Vec::from([1, 2]));
        println!("{:?} {:?}", logs1, logs1.len());
        assert_eq!(logs1.len(), 1);

        let search_terms2 = vec!["     Weeks".to_string()];
        let logs2 = _filter_logs(logs.clone(), search_terms2, Vec::from([]));
        println!("{:?} {:?}", logs2, logs2.len());
        assert_eq!(logs2.len(), 0);

        let search_terms3 = vec!["     Weeks".to_string(), "DEBUG".to_string()];
        let logs3 = _filter_logs(logs.clone(), search_terms3, Vec::from([1, 2]));
        println!("{:?} {:?}", logs3, logs3.len());
        assert_eq!(logs3.len(), 3);
    }
}
